package algorithms;

import game.AbstractOthelloPosition;
import game.OthelloAction;

import java.util.LinkedList;

public class OthelloEvaluatorMobility implements OthelloEvaluatorInterface{

	private static final int TOTAL = (int) Math.pow(AbstractOthelloPosition.BOARD_SIZE,2);
	private static final int OPENING = 8;
	private static final int END = 40;
	private boolean _player;
	
	public OthelloEvaluatorMobility(boolean player){
		_player = player;
	}
	
	@Override
	public int evaluate(AbstractOthelloPosition position) {
		char playerColor = (_player) ? 'W' : 'B';
		char opponentColor = (_player) ? 'B' : 'W';
		AbstractOthelloPosition p = (AbstractOthelloPosition) position.clone();
		p.changePlayer();
		
		LinkedList<OthelloAction> playerMoves;
		LinkedList<OthelloAction> opponentMoves;
		
		// Determine which configuration to use depending of the player who has started the algorithm
		if(_player == position.toMove()){
			playerMoves = position.getMoves();
			opponentMoves = p.getMoves();
		} else{
			playerMoves = p.getMoves();
			opponentMoves = position.getMoves();
		}
		// If the end could be reached
		if(playerMoves.isEmpty() && opponentMoves.isEmpty()){
			int value = position.getCount(playerColor) - position.getCount(opponentColor);
			int toAdd = 1000000;
			if(value < 0){
				toAdd = -toAdd;
			}
			return value + toAdd;
		}else{
			int coeffCount;
			int coeffMobility;
			int coeffStability;
			if(TOTAL - p.getCount('E') < OPENING){
				coeffCount = 2;
				coeffStability = 85;
				coeffMobility = 10;
			}else if(TOTAL - p.getCount('E') >= OPENING && TOTAL - p.getCount('E') < END){
				coeffCount = 30;
				coeffStability = 80;
				coeffMobility = 20;
			}else{
				coeffCount = 80;
				coeffStability = 10;
				coeffMobility = 20;
			}
			int discCount = position.getCount(playerColor) - position.getCount(opponentColor);
			int mobilty = playerMoves.size() - opponentMoves.size();
			int stability = (getNumberOfStableDisc(position, playerColor) - getNumberOfStableDisc(position, opponentColor)) * 16 / 3;
			//return mobility * 20 / 100 + stability + moves * 80/100;
			return discCount * coeffCount + stability * coeffStability + mobilty * coeffMobility;
		}
	}
	
	 /**
	 * Get the number of disc which can not be taken by the opponent
	 * @param p The current Othello position
	 * @param color the player's color
	 * @return
	 */
	private int getNumberOfStableDisc(AbstractOthelloPosition p, char color)
     {
         return getStableFromCorner(p, color, 1, 1)
             + getStableFromCorner(p, color, 1, AbstractOthelloPosition.BOARD_SIZE)
             + getStableFromCorner(p, color, AbstractOthelloPosition.BOARD_SIZE, 1)
             + getStableFromCorner(p, color, AbstractOthelloPosition.BOARD_SIZE, AbstractOthelloPosition.BOARD_SIZE)
             + getStableFromEdge(p, color, 1, true)
             + getStableFromEdge(p, color, AbstractOthelloPosition.BOARD_SIZE, true)
             + getStableFromEdge(p, color, 1, false)
             + getStableFromEdge(p, color, AbstractOthelloPosition.BOARD_SIZE, false);
     }

	/**
	 * Get the number of stable discs from the edge of the board
	 * @param p the current Othello position
	 * @param color the player's color
	 * @param border the index of the row/column to check
	 * @param isHorizontal true if checking a row, false for a column
	 * @return
	 */
	private int getStableFromEdge(AbstractOthelloPosition p, char color, int border, boolean isHorizontal) {
		 int stableDiscs = 0;

         if (isBorderFull(p, border, isHorizontal))
         {
             boolean opponentDiscPassed = false;
             for (int otherCoordinate = 1; otherCoordinate <= AbstractOthelloPosition.BOARD_SIZE; otherCoordinate++)
             {                
                 char colorField = (isHorizontal) ? p.getBoard()[border][otherCoordinate] : p.getBoard()[otherCoordinate][border];
                 if (colorField != color){
                     opponentDiscPassed = true;
                 }else if (opponentDiscPassed)
                 {
                     int numberOfconsecutiveDisc = 0;
                     while ((otherCoordinate <= AbstractOthelloPosition.BOARD_SIZE) && (colorField == color))
                     {
                         numberOfconsecutiveDisc++;

                         otherCoordinate++;
                         if (otherCoordinate <= AbstractOthelloPosition.BOARD_SIZE){
                             colorField = (isHorizontal) ? p.getBoard()[border][otherCoordinate] : p.getBoard()[otherCoordinate][border];
                         }
                     }
                     if (otherCoordinate != AbstractOthelloPosition.BOARD_SIZE + 1)
                     {
                         stableDiscs += numberOfconsecutiveDisc;
                         opponentDiscPassed = true;
                     }                                             
                 }
             }
         }

         return stableDiscs;
	}

	/**
	 * Check if a the edge is full
	 * @param p the current Othello position
	 * @param border the index of the edge to check
	 * @param isHorizontal true if checking a row, false for a column
	 * @return
	 */
	private boolean isBorderFull(AbstractOthelloPosition p, int border,	boolean isHorizontal) {
		  for (int otherCoordinate = 1; otherCoordinate <= AbstractOthelloPosition.BOARD_SIZE; otherCoordinate++){
              if (isHorizontal && (p.getBoard()[border][otherCoordinate] == 'E')
                  || !isHorizontal && (p.getBoard()[otherCoordinate][border] == 'E')){
                  return false;
              }
          }
          return true;
	}

	/**
	 * Get the number of disc which can not be taken by the opponent from the corner
	 * @param p the current Othello position
	 * @param color the player's color
	 * @param cRowIndex row index for the corner (could be 1 or board size)
	 * @param cColumnIndex column index for the corner (could be 1 or board size)
	 * @return
	 */
	private int getStableFromCorner(AbstractOthelloPosition p, char color,	int cRowIndex, int cColumnIndex) {
		 int stableDiscs = 0;

         int rowChanging = (cRowIndex == 1) ? 1 : -1;
         int columnChanging = (cColumnIndex == 1) ? 1 : -1;

         int row = cRowIndex;
         int rowLimit = (cRowIndex == 1) ? AbstractOthelloPosition.BOARD_SIZE : 1;
         int columnLimit = (cColumnIndex == 1) ? AbstractOthelloPosition.BOARD_SIZE : 1;
         for (row = cRowIndex; row != rowLimit; row += rowChanging){
             int column;
             for (column = cColumnIndex; column != columnLimit + 1; column += columnChanging){
                 if (p.getBoard()[row][column] == color){
                     stableDiscs++;
                 }
                 else{
                     break;
                 }
             }
             if ((columnChanging > 0 && column <= AbstractOthelloPosition.BOARD_SIZE) || (columnChanging < 0 && column >= 1)){
                 columnLimit = column - columnChanging;
                 if (columnChanging > 0 && columnLimit == 0){
                     columnLimit++;
                 }else if (columnChanging < 0 && columnLimit == AbstractOthelloPosition.BOARD_SIZE){
                     columnLimit--;
                 }
                 if ((columnChanging > 0 && columnLimit < 0)
                     || (columnChanging < 0 && columnLimit > AbstractOthelloPosition.BOARD_SIZE))
                 {
                     break;
                 }
             }
         }
         return stableDiscs;
	}
	
}